use std::cmp::Ordering;
use crate::co;
use crate::decl::*;
use crate::guard::*;
use crate::kernel::{ffi, privs::*};
use crate::prelude::*;
#[derive(Default, Clone)]
pub struct WString {
buf: Buffer,
}
impl std::fmt::Display for WString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let txt = match self.buf.to_string_checked() {
Ok(t) => t,
Err(e) => format!("PARSING ERROR: {}", e.to_string()),
};
std::fmt::Display::fmt(&txt, f)
}
}
impl std::fmt::Debug for WString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(&self.buf, f)
}
}
impl std::cmp::PartialEq for WString {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == Ordering::Equal
}
}
impl std::cmp::Eq for WString {}
impl std::cmp::PartialOrd for WString {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
let ord = unsafe { ffi::lstrcmpW(self.as_ptr(), other.as_ptr()) };
Some(if ord < 0 {
Ordering::Less
} else if ord > 0 {
Ordering::Greater
} else {
Ordering::Equal
})
}
}
impl std::cmp::Ord for WString {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap()
}
}
impl WString {
#[must_use]
pub fn from_opt_str(s: Option<impl AsRef<str>>) -> Self {
Self { buf: Buffer::from_opt_str(s) }
}
#[must_use]
pub fn from_str(s: impl AsRef<str>) -> Self {
Self { buf: Buffer::from_str(s) }
}
#[must_use]
pub fn from_str_vec(v: &[impl AsRef<str>]) -> Self {
Self { buf: Buffer::from_str_vec(v) }
}
#[must_use]
pub fn from_wchars_count(src: *const u16, num_chars: usize) -> Self {
Self { buf: Buffer::from_wchars_count(src, num_chars) }
}
#[must_use]
pub unsafe fn from_wchars_nullt(src: *const u16) -> Self {
Self { buf: Buffer::from_wchars_nullt(src) }
}
#[must_use]
pub fn from_wchars_slice(src: &[u16]) -> Self {
Self { buf: Buffer::from_wchars_slice(src) }
}
#[must_use]
pub fn new_alloc_buf(sz: usize) -> Self {
Self { buf: Buffer::new_alloc_buf(sz) }
}
#[must_use]
pub unsafe fn as_mut_ptr(&mut self) -> *mut u16 {
self.buf.as_mut_ptr()
}
#[must_use]
pub fn as_mut_slice(&mut self) -> &mut [u16] {
self.buf.as_mut_slice()
}
#[must_use]
pub fn as_ptr(&self) -> *const u16 {
self.buf.as_ptr()
}
#[must_use]
pub fn as_slice(&self) -> &[u16] {
self.buf.as_slice()
}
#[must_use]
pub const fn buf_len(&self) -> usize {
self.buf.buf_len()
}
pub fn copy_to_slice(&self, dest: &mut [u16]) {
if !dest.is_empty() {
let usable_len = dest.len() - 1; self.as_slice()
.iter()
.zip(dest[..usable_len].iter_mut())
.for_each(|(src, dest)| *dest = *src);
dest[usable_len..].iter_mut()
.for_each(|dest| *dest = 0x0000); }
}
pub fn fill_with_zero(&mut self) {
self.as_mut_slice()
.iter_mut()
.for_each(|ch| *ch = 0x0000);
}
#[must_use]
pub const fn is_allocated(&self) -> bool {
self.buf.is_allocated()
}
#[must_use]
pub fn to_string_checked(&self
) -> Result<String, std::string::FromUtf16Error>
{
self.buf.to_string_checked()
}
#[must_use]
pub fn str_len(&self) -> usize {
unsafe { ffi::lstrlenW(self.buf.as_ptr()) as _ }
}
pub fn make_lowercase(&mut self) {
unsafe { ffi::CharLowerW(self.as_mut_ptr()); }
}
pub fn make_uppercase(&mut self) {
unsafe { ffi::CharUpperW(self.as_mut_ptr()); }
}
#[must_use]
pub fn parse(data: &[u8]) -> SysResult<Self> {
let mut data = data;
if data.is_empty() { return Ok(Self::default());
}
let (encoding, sz_bom) = Encoding::guess(data);
data = &data[sz_bom..]; Ok(Self::from_wchars_slice(
&match encoding {
Encoding::Ansi => Self::parse_ansi(data),
Encoding::Win1252 => MultiByteToWideChar(co::CP::WINDOWS_1252, co::MBC::NoValue, data)?,
Encoding::Utf8 => MultiByteToWideChar(co::CP::UTF8, co::MBC::NoValue, data)?,
Encoding::Utf16be => Self::parse_utf16(data, true),
Encoding::Utf16le => Self::parse_utf16(data, false),
Encoding::Utf32be
| Encoding::Utf32le
| Encoding::Scsu
| Encoding::Bocu1
| Encoding::Unknown => panic!("Encoding {} not implemented.", encoding),
}
))
}
fn parse_ansi(data: &[u8]) -> Vec<u16> {
data.iter()
.take_while(|ch| **ch != 0x0000) .map(|ch| *ch as u16) .collect()
}
fn parse_utf16(data: &[u8], is_big_endian: bool) -> Vec<u16> {
let data = if data.len() % 2 == 1 {
&data[..data.len() - 1] } else {
data
};
data.chunks(2)
.take_while(|ch2| **ch2 != [0x00, 0x00]) .map(|ch2| {
if is_big_endian {
u16::from_be_bytes(ch2.try_into().unwrap())
} else {
u16::from_le_bytes(ch2.try_into().unwrap())
}
})
.collect()
}
}
enum Buffer {
Stack([u16; SSO_LEN]),
Heap(usize, GlobalFreeGuard), Unallocated,
}
impl Default for Buffer {
fn default() -> Self {
Self::Unallocated
}
}
impl Clone for Buffer {
fn clone(&self) -> Self {
match self {
Self::Unallocated => Self::Unallocated,
_ => {
let mut new_self = Self::new_alloc_buf(self.buf_len());
self.as_slice()
.iter()
.zip(new_self.as_mut_slice())
.for_each(|(src, dest)| *dest = *src);
new_self
},
}
}
}
impl std::fmt::Debug for Buffer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let txt = match self.to_string_checked() {
Ok(t) => t,
Err(e) => format!("PARSING ERROR: {}", e.to_string()),
};
write!(f, "{}", match self {
Self::Stack(_) => format!("STACK({}) \"{}\"", self.buf_len(), txt),
Self::Heap(_, _) => format!("HEAP({}) \"{}\"", self.buf_len(), txt),
Self::Unallocated => "UNALLOCATED \"\"".to_owned(),
})
}
}
impl Buffer {
#[must_use]
fn from_opt_str(s: Option<impl AsRef<str>>) -> Self {
match s {
Some(s) => Self::from_str(s),
None => Self::Unallocated,
}
}
#[must_use]
fn from_str(s: impl AsRef<str>) -> Self {
let s_len = s.as_ref().encode_utf16().count();
if s_len == 0 {
Self::Unallocated
} else {
let num_chars = s_len + 1; let mut new_self = Self::new_alloc_buf(num_chars);
s.as_ref()
.encode_utf16()
.zip(new_self.as_mut_slice())
.for_each(|(src, dest)| *dest = src);
new_self
}
}
#[must_use]
fn from_str_vec(v: &[impl AsRef<str>]) -> Self {
let tot_chars = v.iter() .fold(0, |tot, s| tot + s.as_ref().chars().count() + 1) + 1; let mut new_self = Self::new_alloc_buf(tot_chars);
v.iter()
.map(|s| {
s.as_ref()
.encode_utf16()
.chain(std::iter::once(0x0000)) })
.flatten()
.zip(new_self.as_mut_slice())
.for_each(|(src, dest)| *dest = src);
new_self
}
#[must_use]
fn from_wchars_count(src: *const u16, num_chars: usize) -> Self {
if src.is_null() || num_chars == 0 {
Self::Unallocated
} else {
Self::from_wchars_slice(
unsafe { std::slice::from_raw_parts(src, num_chars) },
)
}
}
#[must_use]
unsafe fn from_wchars_nullt(src: *const u16) -> Self {
Self::from_wchars_count(src, unsafe { ffi::lstrlenW(src) as _ })
}
#[must_use]
fn from_wchars_slice(src: &[u16]) -> Self {
if src.is_empty() {
Self::Unallocated
} else {
let num_chars = src.iter()
.take_while(|ch| **ch != 0x0000) .count()
+ 1; let mut new_self = Self::new_alloc_buf(num_chars);
src.iter()
.take_while(|ch| **ch != 0x0000) .zip(new_self.as_mut_slice())
.for_each(|(src, dest)| *dest = *src);
new_self
}
}
#[must_use]
fn new_alloc_buf(num_chars: usize) -> Self {
if num_chars == 0 {
Self::Unallocated
} else if num_chars <= SSO_LEN {
Self::Stack([0x0000; SSO_LEN])
} else {
Self::Heap(
num_chars * std::mem::size_of::<u16>(),
HGLOBAL::GlobalAlloc(
Some(co::GMEM::FIXED | co::GMEM::ZEROINIT),
num_chars * std::mem::size_of::<u16>(),
).unwrap(), )
}
}
#[must_use]
unsafe fn as_mut_ptr(&mut self) -> *mut u16 {
match self {
Self::Stack(arr) => arr.as_mut_ptr(),
Self::Heap(_, ptr) => ptr.ptr() as _,
Self::Unallocated => panic!("Trying to use an unallocated WString buffer."),
}
}
#[must_use]
fn as_mut_slice(&mut self) -> &mut [u16] {
match self {
Self::Stack(arr) => arr,
Self::Heap(_, ptr) => unsafe {
std::slice::from_raw_parts_mut(ptr.ptr() as _, self.buf_len())
},
Self::Unallocated => &mut [],
}
}
#[must_use]
fn as_ptr(&self) -> *const u16 {
match self {
Self::Stack(arr) => arr.as_ptr(),
Self::Heap(_, ptr) => ptr.ptr() as _,
Self::Unallocated => std::ptr::null(),
}
}
#[must_use]
fn as_slice(&self) -> &[u16] {
match self {
Self::Stack(arr) => arr,
Self::Heap(_, ptr) => unsafe {
std::slice::from_raw_parts(ptr.ptr() as _, self.buf_len())
},
Self::Unallocated => &[],
}
}
#[must_use]
const fn buf_len(&self) -> usize {
match self {
Self::Stack(arr) => arr.len(),
Self::Heap(sz_bytes, _) => *sz_bytes / std::mem::size_of::<u16>(),
Self::Unallocated => 0,
}
}
#[must_use]
const fn is_allocated(&self) -> bool {
match self {
Self::Unallocated => false,
_ => true,
}
}
#[must_use]
fn to_string_checked(&self) -> Result<String, std::string::FromUtf16Error> {
match self {
Self::Unallocated => Ok(String::default()),
_ => String::from_utf16(
&self.as_slice()
.into_iter()
.take_while(|ch| **ch != 0x0000) .map(|ch| *ch)
.collect::<Vec<_>>(),
),
}
}
}